/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.l2jserver.gameserver.instancemanager;

import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.datatables.CharNameTable;
import com.l2jserver.gameserver.datatables.ItemTable;
import com.l2jserver.gameserver.instancemanager.CustomItemMallManager.MallItemTemplate.ItemPackage;
import com.l2jserver.gameserver.model.BlockList;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.entity.Message;
import com.l2jserver.gameserver.model.itemcontainer.Mail;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.serverpackets.ExBrBuyProduct;
import com.l2jserver.gameserver.network.serverpackets.ExBrBuyProduct.ResultCode;
import com.l2jserver.gameserver.network.serverpackets.ExBrGamePoint;
import com.l2jserver.gameserver.network.serverpackets.ExBrProductInfo;
import com.l2jserver.gameserver.network.serverpackets.ExBrProductList;
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;


/**
 * @author liuj
 */
public class CustomItemMallManager
{
	
	public static class MallItem
	{
		private int mallItemId;
		private int point;
		private int category;
		private long startTime;
		private long endTime;
		private int startHour = 0;
		private int startMin = 0;
		private int endHour = 23;
		private int endMin = 59;
		private int saleflag;
		private volatile int stock;
		private boolean stockdirty = false;
		private int maxStock;
		
		private MallItemTemplate template;
		
		private final long UNLIMITED_START_TIME = 315558000000L;
		private final long UNLIMITED_END_TIME = 2127452400000L;
		
		/**
		 * @return the mallItemId
		 */
		public int getMallItemId() {
			return mallItemId;
		}

		/**
		 * @param mallItemId the mallItemId to set
		 */
		public void setMallItemId(int mallItemId) {
			this.mallItemId = mallItemId;
		}

		/**
		 * @return the point
		 */
		public int getPoint() {
			return point;
		}

		/**
		 * @param point the point to set
		 */
		public void setPoint(int point) {
			this.point = point;
		}

		/**
		 * @return the category
		 */
		public int getCategory() {
			return category;
		}

		/**
		 * @param category the category to set
		 */
		public void setCategory(int category) {
			this.category = category;
		}

		/**
		 * @return the startTime
		 */
		public long getStartTime() {
			return startTime;
		}

		/**
		 * @param startTime the startTime to set
		 */
		public void setStartTime(long startTime) {
			if(startTime>0){
				this.startTime = startTime;
			}else{
				this.startTime = UNLIMITED_START_TIME;
			}

		}

		/**
		 * @return the endTime
		 */
		public long getEndTime() {
			return endTime;
		}

		/**
		 * @param endTime the endTime to set
		 */
		public void setEndTime(long endTime) {
			if(endTime>0){
				this.endTime = endTime;
			}
			else{
				this.endTime = UNLIMITED_END_TIME;
			}
		}

		/**
		 * @param startHour the startHour to set
		 */
		public void setStartHour(int startHour) {
			if(startHour < 0 || startHour >23){
				this.startHour = 0;
			}else{
				this.startHour = startHour;
			}
		}

		/**
		 * @param startMin the startMin to set
		 */
		public void setStartMin(int startMin) {
			if(startMin <0 || startMin >59){
				this.startMin = 0;
			}else{
				this.startMin = startMin;
			}
		}

		/**
		 * @param endHour the endHour to set
		 */
		public void setEndHour(int endHour) {
			if(endHour <0 || endHour>23){
				this.endHour = 23;
			}else{
				this.endHour = endHour;
			}
		}

		/**
		 * @param endMin the endMin to set
		 */
		public void setEndMin(int endMin) {
			if(endMin <0 || endMin>59){
				this.endMin = 59;
			}else{
				this.endMin = endMin;
			}
		}

		/**
		 * @return the saleflag
		 */
		public int getSaleflag() {
			return saleflag;
		}

		/**
		 * @param saleflag the saleflag to set
		 */
		public void setSaleflag(int saleflag) {
			this.saleflag = saleflag;
		}

		/**
		 * @return the stock
		 */
		public int getStock() {
			return stock;
		}

		/**
		 * @param stock the stock to set
		 */
		public void setStock(int stock) {
			this.stock = stock;
		}

		/**
		 * @return the maxStock
		 */
		public int getMaxStock() {
			return maxStock;
		}

		/**
		 * @param maxStock the maxStock to set
		 */
		public void setMaxStock(int maxStock) {
			this.maxStock = maxStock;
		}

		/**
		 * @return the startHour
		 */
		public int getStartHour() {
			return startHour;
		}

		/**
		 * @return the startMin
		 */
		public int getStartMin() {
			return startMin;
		}

		/**
		 * @return the endHour
		 */
		public int getEndHour() {
			return endHour;
		}

		/**
		 * @return the endMin
		 */
		public int getEndMin() {
			return endMin;
		}

		/**
		 * @return the template
		 */
		public MallItemTemplate getTemplate() {
			return template;
		}
		
		public MallItem(MallItemTemplate t)
		{
			template = t;
			startTime = UNLIMITED_START_TIME;
			endTime = UNLIMITED_END_TIME;
		}
		
		public boolean increaseStock(int count){
			synchronized (this) {
				if(stock + count <= maxStock){
					stock += count;
					stockdirty = true;
					return true;
				}
			}
			return false;
		}
		
		public boolean dirtyStock(){
			if(stockdirty){
				stockdirty = false;
				return true;
			}
			return stockdirty;
		}
		
	}
	
	public static class MallItemTemplate
	{
		private int templateId;
		private List<ItemPackage> itemPackage;
		
		/**
		 * @return the templateId
		 */
		public int getTemplateId() {
			return templateId;
		}

		/**
		 * @param templateId the templateId to set
		 */
		public void setTemplateId(int templateId) {
			this.templateId = templateId;
		}

		/**
		 * @return the itemPackage
		 */
		public List<ItemPackage> getItemPackage() {
			return itemPackage;
		}

		/**
		 * @param itemPackage the itemPackage to set
		 */
		public void setItemPackage(List<ItemPackage> itemPackage) {
			this.itemPackage = itemPackage;
		}

		public static class ItemPackage{
			private int itemId;
			private int count;
			private int weight;
			private boolean dropable;
			private boolean stackable;
			
			/**
			 * @return the itemId
			 */
			public int getItemId() {
				return itemId;
			}
			/**
			 * @param itemId the itemId to set
			 */
			public void setItemId(int itemId) {
				this.itemId = itemId;
			}
			/**
			 * @return the count
			 */
			public int getCount() {
				return count;
			}
			/**
			 * @param count the count to set
			 */
			public void setCount(int count) {
				this.count = count;
			}
			/**
			 * @return the weight
			 */
			public int getWeight() {
				return weight;
			}
			/**
			 * @param weight the weight to set
			 */
			public void setWeight(int weight) {
				this.weight = weight;
			}
			
			/**
			 * @return the dropable
			 */
			public boolean isDropable() {
				return dropable;
			}
			/**
			 * @param dropable the dropable to set
			 */
			public void setDropable(boolean dropable) {
				this.dropable = dropable;
			}
			
			/**
			 * @return the stackable
			 */
			public boolean isStackable() {
				return stackable;
			}
			/**
			 * @param stackable the stackable to set
			 */
			public void setStackable(boolean stackable) {
				this.stackable = stackable;
			}
			
		}
	}
	
	private static Logger _log = Logger.getLogger(CustomItemMallManager.class.getName());
	private static CustomItemMallManager _instance = new CustomItemMallManager();
	
	public static CustomItemMallManager getInstance()
	{
		return _instance;
	}
	
	public final int MAX_BUY_COUNT = 99;
	private Map<Integer, MallItemTemplate> Templates;
	private final Map<Integer, MallItem> shopItems;
	private final List<MallItem> shopItemList;
	
	private CustomItemMallManager()
	{
		Templates = new HashMap<Integer, MallItemTemplate>();
		shopItems = new HashMap<Integer, MallItem>();
		shopItemList = new ArrayList<MallItem>();
		load();
		Templates = null;
		ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new Runnable() {
			
			@Override
			public void run() {
				for(MallItem mItem : shopItemList){
					if(mItem.dirtyStock()){
						Connection con = null;
						 try {
							con = L2DatabaseFactory.getInstance().getConnection();
							final PreparedStatement statement = con.prepareStatement("UPDATE custom_item_mall SET stock=? WHERE mallItemId=?");
							statement.setInt(1, mItem.getStock());
							statement.setInt(2, mItem.getMallItemId());
							statement.execute();
							statement.close();
						} catch (SQLException e) {
							_log.log(Level.WARNING, "Could not store custom_item_mall stock data: "+this+" - " + e.getMessage(), e);
						}finally{
							L2DatabaseFactory.close(con);
						}
					}
				}
			}
		}, 600000, 600000);
	}
	
	private void load()
	{
		loadTempaltes();
		loadShop();
	}
	
	private void loadTempaltes() 
	{
		try
		{
			DocumentBuilderFactory e = DocumentBuilderFactory.newInstance();
			e.setValidating(false);
			e.setIgnoringComments(true);
			File file = new File(Config.DATAPACK_ROOT + "/data/item-mall.xml");
			Document doc = e.newDocumentBuilder().parse(file);
			
			for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
			{
				if ("list".equalsIgnoreCase(n.getNodeName()))
				{
					for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
					{
						if ("template".equalsIgnoreCase(d.getNodeName()))
						{
							MallItemTemplate mtp = new MallItemTemplate();
							NamedNodeMap attrs = d.getAttributes();
							String tempId = attrs.getNamedItem("id").getNodeValue();
							mtp.setTemplateId(Integer.parseInt(tempId));
							mtp.setItemPackage(new ArrayList<MallItemTemplate.ItemPackage>());
							Templates.put(mtp.getTemplateId(), mtp);
	
							for(Node d1 = d.getFirstChild();d1!=null;d1 = d1.getNextSibling()){
								if("item".equalsIgnoreCase(d1.getNodeName())){
									MallItemTemplate.ItemPackage pItem = new MallItemTemplate.ItemPackage();
									NamedNodeMap item = d1.getAttributes();
									String itemID = item.getNamedItem("id").getNodeValue();
									String itemCount = item.getNamedItem("count").getNodeValue();
									pItem.setItemId(Integer.parseInt(itemID));
									pItem.setCount(Integer.parseInt(itemCount));
									L2ItemInstance dummy = ItemTable.getInstance().createDummyItem(pItem.getItemId());
									pItem.setWeight(dummy.getItem().getWeight());
									pItem.setDropable(dummy.getItem().isDropable());
									pItem.setStackable(dummy.getItem().isStackable());
									mtp.getItemPackage().add(pItem);
								}
							}
						}
					}
				}
			}
			
		}
		catch (Exception e)
		{
			_log.severe("CustomItemMallManager: Error parsing item-mall.xml file. ");
		}
		
		_log.info("INFO 道具商城 读取 " + Templates.size() + "个模板物品");
	}
	
	private long getMillisecondByDate(Date date){
		if(date!=null){
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(date);
			return calendar.getTimeInMillis();
		}
		return 0;
	}
	
	private void loadShop()
	{
		Connection con = null;
		PreparedStatement statement = null;
		ResultSet result = null;
		
		try
		{
			con = L2DatabaseFactory.getInstance().getConnection();
			statement = con.prepareStatement("SELECT * FROM custom_item_mall WHERE enable<>0 ORDER BY `order` ASC");
			result = statement.executeQuery();
			
			while (result.next())
			{
				int tempId = result.getInt("templateId");
				MallItemTemplate tempData = Templates.get(tempId);
				if (tempData == null)
				{
					_log.warning("CustomItemMallManager: item template for " + tempId + " was not found. skipping.");
				}
				else
				{
					MallItem item = new MallItem(tempData);
					item.setMallItemId(result.getInt("mallItemId"));
					item.setPoint(result.getInt("point"));
					item.setCategory(result.getInt("category"));
					item.setSaleflag(result.getInt("saleflag"));
					item.setStock(result.getInt("stock"));
					item.setMaxStock(result.getInt("maxStock"));
					item.setStartTime(getMillisecondByDate(result.getDate("startTime")));
					item.setEndTime(getMillisecondByDate(result.getDate("endTime")));
					item.setStartHour(result.getInt("startHour"));
					item.setStartMin(result.getInt("startMin"));
					item.setEndHour(result.getInt("endHour"));
					item.setEndMin(result.getInt("endMin"));
					shopItemList.add(item);
					shopItems.put(item.getMallItemId(), item);
				}
				
			}
		}
		catch (Exception e)
		{
			_log.warning("CustomItemMallManager: error in loadShop() " + e);
		}
		finally
		{
			try
			{
				con.close();
				statement.close();
				result.close();
			}
			catch (Exception e2)
			{
				_log.warning("CustomItemMallManager: close database exception. " + e2);
			}
		}
		
		_log.info("INFO 道具商城 读取 " + shopItemList.size() + "个可在线交易物品");
	}
	
	
	public void requestBuyItem(L2PcInstance player, int mallItemId, int count)
	{
		BuyItem(player, mallItemId, count, null, null, null);
	}
	
	public void requestBuyItemForPresent(L2PcInstance player, int mallItemId, int count, 
			String recipient, String topic,String msg){
		BuyItem(player, mallItemId, count, recipient, topic, msg);
	}
	
	@SuppressWarnings("deprecation")
	private void BuyItem(L2PcInstance player, int mallItemId, int count,
			String recipient, String topic,String msg)
	{
		if (count > MAX_BUY_COUNT)
		{
			count = MAX_BUY_COUNT;
		}
		if (count < 1)
		{
			count = 1;
		}

		MallItem item = shopItems.get(mallItemId);
		if (item == null)
		{
			sendResult(player, ResultCode.RESULT_WRONG_PRODUCT);
			return;
		}
		
		if (System.currentTimeMillis() < item.getStartTime())
		{
			sendResult(player,ResultCode.RESULT_SALE_NO_START);
			return;
		}
		
		if(System.currentTimeMillis() > item.getEndTime()){
			sendResult(player,ResultCode.RESULT_SALE_END);
			return;
		}
		
		Date currentTime = new Date();
		if(currentTime.getHours() > item.getStartHour() && currentTime.getHours() < item.getEndHour()){
			//OK
		}else{
			if(currentTime.getHours() == item.getStartHour() ||
					currentTime.getHours() == item.getEndHour()){
				
				if(currentTime.getHours() == item.getStartHour()){
					if(currentTime.getMinutes() < item.getStartMin()){
						sendResult(player,ResultCode.RESULT_WRONG_SALE_TIME);
						return;
					}
				}
				if(currentTime.getHours() == item.getEndHour()){
					if(currentTime.getMinutes() > item.getEndMin()){
						sendResult(player,ResultCode.RESULT_WRONG_SALE_TIME);
						return;
					}
				}
			}else{
				sendResult(player,ResultCode.RESULT_WRONG_SALE_TIME);
				return;
			}
		}
		
		int costPoints = item.getPoint() * count;
		if (costPoints < 0)
		{
			sendResult(player,ResultCode.RESULT_WRONG_PRODUCT);
			return;
		}
		
		if(costPoints > player.getGamePoint()){
			sendResult(player,ResultCode.RESULT_NOT_ENOUGH_POINT);
			return;
		}
		
		int totalWeight = 0;
		int totalItemCount = 0;
		for (ItemPackage pk : item.getTemplate().getItemPackage())
		{
			totalWeight += pk.getWeight();
			totalItemCount += pk.isStackable() ? 1 : pk.getCount() * count;
		}
		totalWeight *= count;
		
		int targetId = -1;
		if(recipient == null){//self
			if(!player.getInventory().validateCapacity(totalItemCount) || 
					!player.getInventory().validateWeight(totalWeight)){
				sendResult(player,ResultCode.RESULT_INVENTORY_FULL);
				return;
			}
		}else{//for gift
			targetId = CharNameTable.getInstance().getIdByName(recipient);
			if(targetId>0){
				if(BlockList.isInBlockList(targetId, player.getObjectId())){
					player.sendPacket(SystemMessage.getSystemMessage(1228).addString(recipient));
					return;
				}
				
				if(targetId == player.getObjectId()){
					player.sendPacket(SystemMessageId.YOU_CANT_SEND_MAIL_TO_YOURSELF);
					return;
				}
			}else{
				player.sendPacket(SystemMessageId.RECIPIENT_NOT_EXIST);
				return;
			}
		}

		
		if(item.getMaxStock() !=-1){
			if(!item.increaseStock(count)){
				sendResult(player,ResultCode.RESULT_NOT_ENOUGH_STOCK);
				return;
			}
		}

		if(player.costGamePoint(costPoints)){
			if(recipient == null){
				for (ItemPackage pk : item.getTemplate().getItemPackage())
				{
					int pkItemCount = pk.getCount() * count;
					player.getInventory().addItem("MallItem buy", pk.getItemId(), pkItemCount, player, null);
					if(pk.isStackable()){
						player.sendPacket((SystemMessage.getSystemMessage(53)).addItemName(Integer.valueOf(pk.getItemId())).addNumber(Integer.valueOf(pkItemCount)));
					}else{
						for(int i=0;i<pkItemCount;i++){
							player.sendPacket((SystemMessage.getSystemMessage(54)).addItemName(pk.getItemId()));
						}
					}
				}
			}else{
				List<Message> mailList = new ArrayList<>();
				int pkc = 0;
				Mail attachments = null;
				for (ItemPackage pk : item.getTemplate().getItemPackage())
				{
					int pkItemCount = pk.getCount() * count;
					if(pkc == 0){
						Message mail = new Message(player.getObjectId(),targetId,false,topic,msg,0);
						attachments = mail.createAttachments();
						mailList.add(mail);
					}
					attachments.addItem("MallItem present",pk.getItemId(),pkItemCount, null, null);	
					pkc++;
					if(pkc == 8)
						pkc = 0;
				}
				for(Message mail : mailList){
					MailManager.getInstance().sendMessage(mail);
				}
			}

			player.updateProductHistory(item);
			ShowPoints(player);
			sendResult(player,ResultCode.RESULT_SUCCESS);
			player.sendMessage("购买成功！本次花费了" + costPoints + "点数.");
			player.broadcastStatusUpdate();
		}else{
			sendResult(player,ResultCode.RESULT_NOT_ENOUGH_POINT);
			return;
		}
		
	}
	
	public void showItemInfo(L2PcInstance player, int mallItemId){
		MallItem item = shopItems.get(mallItemId);
		if (item == null){
			sendResult(player, ResultCode.RESULT_WRONG_PRODUCT);
		}else{
			player.sendPacket(new ExBrProductInfo(item));
		}
	}
	
	public void showList(L2PcInstance player){
		player.sendPacket(new ExBrProductList(shopItemList));
	}
	
	public void ShowPoints(L2PcInstance player){
		player.sendPacket(new ExBrGamePoint(player.getObjectId(), player.getGamePoint()));
	}
	
	public void sendResult(L2PcInstance player, ResultCode code)
	{
		player.sendPacket(new ExBrBuyProduct(code));
	}
	
	public MallItem getMallItemById(int MallItemId){
		return shopItems.get(MallItemId);
	}

}
